// --xuezhen-- 写的原版
//20210686追加的注释：Verilator的原理实际上是将Verilog源码编译成单/多线程的C++源代码来进行仿真，其将所需要仿真的dut（Device under test被测器件 被测设备 被测单元）编译为一个类，dut的IO口则被编译为类成员。Verilator不单单只是简单的把Verilog编译为C++，Verilator还会将代码进行优化，编译成优化过的C++模型来进行仿真。
//rvcpu-test.cpp verilator的仿真文件
#include <verilated.h>          // 核心头文件
#include <verilated_vcd_c.h>    // 波形生成头文件
#include <iostream>
#include <fstream>
#include "Vrvcpu.h" // 被测器件模块类 vscode ctrl+p搜不出来，用sudo find -name 'Vrvcpu.h'搜出来被调用到/home/lee/ysyx/verilog/oscpu/projects/cpu/build_test/emu-compile/Vrvcpu.h，这个文件每次都会被反复覆盖，所以要修改这个文件，不要在这个位置修改，而要找到产生它的源头

using namespace std;

static Vrvcpu* top; // 顶层dut（Device under test被测器件 被测设备 被测单元）对象指针
static VerilatedVcdC* tfp; //// 波形生成对象指针？
static vluint64_t main_time = 0;// 仿真时间戳
//static const vluint64_t sim_time = 1000;//原版 // 设置最大仿真时间戳
static const vluint64_t sim_time = 60;

// inst.bin
// inst 0: 1 + zero = reg1 1+0=1
// inst 1: 2 + zero = reg1 2+0=2
// inst 2: 1 + reg1 = reg1 1+2=3
int inst_rom[65536];//65536个元素的int数组

void read_inst( char* filename)
{
  FILE *fp = fopen(filename, "rb");//指向 FILE 对象的指针，该 FILE 对象标识了流
  if( fp == NULL ) {
		printf( "Can not open this file!\n" );
		exit(1);
  }
  
  fseek(fp, 0, SEEK_END);//0是相对SEEK_END的偏移量，以字节为单位,SEEK_END文件的末尾
  size_t size = ftell(fp);
  printf("inst.bin总大小 = %ld 字节\n", size);
  fseek(fp, 0, SEEK_SET);//SEEK_SET	文件的开头 /* 整句为查找文件的开头 */
  size = fread(inst_rom, size, 1, fp);//inst_rom是指向带有最小尺寸 size*1 字节的内存块的指针, fp是指向 FILE 对象的指针，该 FILE 对象指定了一个输入流,成功读取的元素总数会以 size_t 对象返回，size_t 对象是一个整型数据类型。如果总数与 第三个参数 参数不同，则可能发生了一个错误或者到达了文件末尾。
  printf("size总大小 = %ld 字节\n", size);
  ///*
  printf("inst_rom:\n");
  printf("%n\n", inst_rom);  
  int i = 0;
	for (i = 0; i<sizeof(inst_rom) / sizeof(inst_rom[0]); i++)
	{
		if(inst_rom[i]!=0){
			printf("元素%d\n",i);
			printf("%d\n", inst_rom[i]);
		}

	}
	printf("遍历完inst_rom\n");
	//*/
  fclose(fp);
}

int main(int argc, char **argv)
{
	char filename[100];
	//printf("Please enter your filename~\n");//在这里输入inst.bin
	//cin >> filename;
	//read_inst(filename);
	printf("取指阶段时cpu从内存（用inst.bin代替内存）中取出指令\n");
	read_inst("inst.bin");
  // initialization 一些初始化工作
  Verilated::commandArgs(argc, argv);
  Verilated::traceEverOn(true);
  // 为对象分配内存空间
  top = new Vrvcpu;
  tfp = new VerilatedVcdC;
  //tfp初始化工作
  top->trace(tfp, 99);//原版
  tfp->open("top.vcd");
	
	while( !Verilated::gotFinish() && main_time < sim_time )//从0到10 以前是999 被我改了
	{
		//// 仿真过程
	  if( main_time % 10 == 0 ) {top->clk = 0;printf("波形在ps:%ld变成持续下降 即clk0\n",main_time);};
	  if( main_time % 10 == 5 ) {top->clk = 1;printf("\t\t波形在ps:%ld变成持续上升 即clk1\n",main_time);};
		  
	  if( main_time < 10 )
	  {
		top->rst = 1;//rst复位信号，很变化，是不是只用来重启、兼容旧软硬件？那去掉复位信号改用电源开关去开机关机，那是不是能省掉大量的rst复位信号判断加快cpu的速度？
		printf("\t\t当main_time即波形在ps（不是pc程序计数器） < 10在c++里用top->rst = 1传1给verilog的rst\n");

	  }
	  else
	  {
	    top->rst = 0;//复位信号
		printf("当main_time即波形在ps（不是pc程序计数器） >= 10在c++里用top->rst = 0传0给verilog的rst\n");
		if( main_time % 10 == 5 ){
		  printf("\t\ttop->inst指令: %d\n", top->inst);
		  printf("\t\ttop->inst_ena: %d\n", top->inst_ena);
		  printf("\t\ttop->inst_addr指令地址: %ld\n", top->inst_addr);
		  printf("\t\t(top->inst_addr) >> 2: %ld\n", (top->inst_addr) >> 2);
		  printf("\t\tinst_rom[ (top->inst_addr指令地址) >> 2 ] : %d\n", inst_rom[ (top->inst_addr) >> 2 ] );
		  top->inst = (top->inst_ena == 1) ? inst_rom[ (top->inst_addr) >> 2 ] : 0;//右平移 ( x >> y ) 等于x除以（2的y次方）
		  printf("\t\tinst_rom[ (top->inst_addr指令地址) >> 2 ] : %d\n", inst_rom[ (top->inst_addr) >> 2 ] );
		  printf("\t\t(top->inst_addr指令地址) >> 2: %ld\n", (top->inst_addr) >> 2);
		  printf("\t\ttop->inst_addr指令地址: %ld\n", top->inst_addr);
		  printf("\t\ttop->inst指令: %d\n", top->inst);
		}
	  }
	  top->eval();// 仿真时间步进
	  tfp->dump(main_time); //// 波形文件写入步进
	  if (main_time % 10<5){
	  	printf("波形里的ps:%ld\n",main_time);
	  }else{
		printf("\t\t波形里的ps:%ld\n",main_time);  
	  }

	  main_time++;
	}
		
  // clean 清理工作
  tfp->close();
  delete top;
  delete tfp;
  exit(0);
  return 0;
}
